home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / sh-utils.12 / sh-utils / sh-utils-1.12 / src / pathchk.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-21  |  9.5 KB  |  378 lines

  1. /* pathchk -- check whether pathnames are valid or portable
  2.    Copyright (C) 91, 92, 93, 1994 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Usage: pathchk [-p] [--portability] path...
  19.  
  20.    For each PATH, print a message if any of these conditions are false:
  21.    * all existing leading directories in PATH have search (execute) permission
  22.    * strlen (PATH) <= PATH_MAX
  23.    * strlen (each_directory_in_PATH) <= NAME_MAX
  24.  
  25.    Exit status:
  26.    0            All PATH names passed all of the tests.
  27.    1            An error occurred.
  28.  
  29.    Options:
  30.    -p, --portability    Instead of performing length checks on the
  31.             underlying filesystem, test the length of the
  32.             pathname and its components against the POSIX.1
  33.             minimum limits for portability, _POSIX_NAME_MAX
  34.             and _POSIX_PATH_MAX in 2.9.2.  Also check that
  35.             the pathname contains no character not in the
  36.             portable filename character set.
  37.  
  38.    David MacKenzie <djm@gnu.ai.mit.edu>
  39.    and Jim Meyering <meyering@cs.utexas.edu> */
  40.  
  41. #include <config.h>
  42. #include <stdio.h>
  43. #include <getopt.h>
  44. #include <sys/types.h>
  45.  
  46. #include "version.h"
  47. #include "system.h"
  48. #include "safe-stat.h"
  49.  
  50. #ifdef _POSIX_VERSION
  51. #include <limits.h>
  52. #ifndef PATH_MAX
  53. #define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
  54. #endif /* not PATH_MAX */
  55. #ifndef NAME_MAX
  56. #define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX);
  57. #endif /* not NAME_MAX */
  58.  
  59. #else /* not _POSIX_VERSION */
  60.  
  61. #include <sys/param.h>
  62. #ifndef PATH_MAX
  63. #ifdef MAXPATHLEN
  64. #define PATH_MAX MAXPATHLEN
  65. #else /* not MAXPATHLEN */
  66. #define PATH_MAX _POSIX_PATH_MAX
  67. #endif /* not MAXPATHLEN */
  68. #endif /* not PATH_MAX */
  69.  
  70. #ifndef NAME_MAX
  71. #ifdef MAXNAMLEN
  72. #define NAME_MAX MAXNAMLEN
  73. #else /* not MAXNAMLEN */
  74. #define NAME_MAX _POSIX_NAME_MAX
  75. #endif /* not MAXNAMLEN */
  76. #endif /* not NAME_MAX */
  77.  
  78. #endif /* not _POSIX_VERSION */
  79.  
  80. #ifndef _POSIX_PATH_MAX
  81. #define _POSIX_PATH_MAX 255
  82. #endif
  83. #ifndef _POSIX_NAME_MAX
  84. #define _POSIX_NAME_MAX 14
  85. #endif
  86.  
  87. #ifndef PATH_MAX_FOR
  88. #define PATH_MAX_FOR(p) PATH_MAX
  89. #endif
  90. #ifndef NAME_MAX_FOR
  91. #define NAME_MAX_FOR(p) NAME_MAX
  92. #endif
  93.  
  94. char *xstrdup ();
  95. void error ();
  96.  
  97. static int validate_path ();
  98. static void usage ();
  99.  
  100. /* The name this program was run with. */
  101. char *program_name;
  102.  
  103. /* If non-zero, display usage information and exit.  */
  104. static int show_help;
  105.  
  106. /* If non-zero, print the version on standard output and exit.  */
  107. static int show_version;
  108.  
  109. static struct option const longopts[] =
  110. {
  111.   {"help", no_argument, &show_help, 1},
  112.   {"portability", no_argument, NULL, 'p'},
  113.   {"version", no_argument, &show_version, 1},
  114.   {NULL, 0, NULL, 0}
  115. };
  116.  
  117. void
  118. main (argc, argv)
  119.      int argc;
  120.      char **argv;
  121. {
  122.   int exit_status = 0;
  123.   int check_portability = 0;
  124.   int optc;
  125.  
  126.   program_name = argv[0];
  127.  
  128.   while ((optc = getopt_long (argc, argv, "p", longopts, (int *) 0)) != EOF)
  129.     {
  130.       switch (optc)
  131.     {
  132.     case 0:
  133.       break;
  134.  
  135.     case 'p':
  136.       check_portability = 1;
  137.       break;
  138.  
  139.     default:
  140.       usage (1);
  141.     }
  142.     }
  143.  
  144.   if (show_version)
  145.     {
  146.       printf ("pathchk - %s\n", version_string);
  147.       exit (0);
  148.     }
  149.  
  150.   if (show_help)
  151.     usage (0);
  152.  
  153.   if (optind == argc)
  154.     {
  155.       error (0, 0, "too few arguments");
  156.       usage (1);
  157.     }
  158.  
  159.   for (; optind < argc; ++optind)
  160.     exit_status |= validate_path (argv[optind], check_portability);
  161.  
  162.   exit (exit_status);
  163. }
  164.  
  165. /* Each element is nonzero if the corresponding ASCII character is
  166.    in the POSIX portable character set, and zero if it is not.
  167.    In addition, the entry for `/' is nonzero to simplify checking. */
  168. static char const portable_chars[256] =
  169. {
  170.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
  171.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
  172.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
  173.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
  174.   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
  175.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
  176.   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
  177.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
  178.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  179.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  180.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  181.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  182.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  183.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  184.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  185.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  186. };
  187.  
  188. /* If PATH contains only portable characters, return 1, else 0.  */
  189.  
  190. static int
  191. portable_chars_only (path)
  192.      const char *path;
  193. {
  194.   const char *p;
  195.  
  196.   for (p = path; *p; ++p)
  197.     if (portable_chars[(const unsigned char) *p] == 0)
  198.       {
  199.     error (0, 0, "path `%s' contains nonportable character `%c'",
  200.            path, *p);
  201.     return 0;
  202.       }
  203.   return 1;
  204. }
  205.  
  206. /* Return 1 if PATH is a usable leading directory, 0 if not,
  207.    2 if it doesn't exist.  */
  208.  
  209. static int
  210. dir_ok (path)
  211.      const char *path;
  212. {
  213.   struct stat stats;
  214.  
  215.   if (SAFE_STAT (path, &stats))
  216.     return 2;
  217.  
  218.   if (!S_ISDIR (stats.st_mode))
  219.     {
  220.       error (0, 0, "`%s' is not a directory", path);
  221.       return 0;
  222.     }
  223.  
  224.   /* Use access to test for search permission because
  225.      testing permission bits of st_mode can lose with new
  226.      access control mechanisms.  Of course, access loses if you're
  227.      running setuid. */
  228.   if (access (path, X_OK) != 0)
  229.     {
  230.       if (errno == EACCES)
  231.     error (0, 0, "directory `%s' is not searchable", path);
  232.       else
  233.     error (0, errno, "%s", path);
  234.       return 0;
  235.     }
  236.  
  237.   return 1;
  238. }
  239.  
  240. /* Make sure that
  241.    strlen (PATH) <= PATH_MAX
  242.    && strlen (each-existing-directory-in-PATH) <= NAME_MAX
  243.  
  244.    If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
  245.    _POSIX_NAME_MAX instead, and make sure that PATH contains no
  246.    characters not in the POSIX portable filename character set, which
  247.    consists of A-Z, a-z, 0-9, ., _, -.
  248.  
  249.    Make sure that all leading directories along PATH that exist have
  250.    `x' permission.
  251.  
  252.    Return 0 if all of these tests are successful, 1 if any fail. */
  253.  
  254. static int
  255. validate_path (path, portability)
  256.      char *path;
  257.      int portability;
  258. {
  259.   int path_max;
  260.   int last_elem;        /* Nonzero if checking last element of path. */
  261.   int exists;            /* 2 if the path element exists.  */
  262.   char *slash;
  263.   char *parent;            /* Last existing leading directory so far.  */
  264.  
  265.   if (portability && !portable_chars_only (path))
  266.     return 1;
  267.  
  268.   if (*path == '\0')
  269.     return 0;
  270.  
  271. #ifdef lint
  272.   /* Suppress `used before initialized' warning.  */
  273.   exists = 0;
  274. #endif
  275.  
  276.   /* Figure out the parent of the first element in PATH.  */
  277.   parent = xstrdup (*path == '/' ? "/" : ".");
  278.  
  279.   slash = path;
  280.   last_elem = 0;
  281.   while (1)
  282.     {
  283.       int name_max;
  284.       int length;        /* Length of partial path being checked. */
  285.       char *start;        /* Start of path element being checked. */
  286.  
  287.       /* Find the end of this element of the path.
  288.      Then chop off the rest of the path after this element. */
  289.       while (*slash == '/')
  290.     slash++;
  291.       start = slash;
  292.       slash = index (slash, '/');
  293.       if (slash != NULL)
  294.     *slash = '\0';
  295.       else
  296.     {
  297.       last_elem = 1;
  298.       slash = index (start, '\0');
  299.     }
  300.  
  301.       if (!last_elem)
  302.     {
  303.       exists = dir_ok (path);
  304.       if (dir_ok == 0)
  305.         {
  306.           free (parent);
  307.           return 1;
  308.         }
  309.     }
  310.  
  311.       length = slash - start;
  312.       /* Since we know that `parent' is a directory, it's ok to call
  313.      pathconf with it as the argument.  (If `parent' isn't a directory
  314.      or doesn't exist, the behavior of pathconf is undefined.)
  315.      But if `parent' is a directory and is on a remote file system,
  316.      it's likely that pathconf can't give us a reasonable value
  317.      and will return -1.  (NFS and tempfs are not POSIX . . .)
  318.      In that case, we have no choice but to assume the pessimal
  319.      POSIX minimums.  */
  320.       name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
  321.       if (name_max < 0)
  322.     name_max = _POSIX_NAME_MAX;
  323.       if (length > name_max)
  324.     {
  325.       error (0, 0, "name `%s' has length %d; exceeds limit of %d",
  326.          start, length, name_max);
  327.       free (parent);
  328.       return 1;
  329.     }
  330.  
  331.       if (last_elem)
  332.     break;
  333.  
  334.       if (exists == 1)
  335.     {
  336.       free (parent);
  337.       parent = xstrdup (path);
  338.     }
  339.  
  340.       *slash++ = '/';
  341.     }
  342.  
  343.   /* `parent' is now the last existing leading directory in the whole path,
  344.      so it's ok to call pathconf with it as the argument.  */
  345.   path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
  346.   if (path_max < 0)
  347.     path_max = _POSIX_PATH_MAX;
  348.   free (parent);
  349.   if (strlen (path) > path_max)
  350.     {
  351.       error (0, 0, "path `%s' has length %d; exceeds limit of %d",
  352.          path, strlen (path), path_max);
  353.       return 1;
  354.     }
  355.  
  356.   return 0;
  357. }
  358.  
  359. static void
  360. usage (status)
  361.      int status;
  362. {
  363.   if (status != 0)
  364.     fprintf (stderr, "Try `%s --help' for more information.\n",
  365.          program_name);
  366.   else
  367.     {
  368.       printf ("Usage: %s [OPTION]... PATH...\n", program_name);
  369.       printf ("\
  370. \n\
  371.   -p, --portability   check for all POSIX systems, not only this one\n\
  372.       --help          display this help and exit\n\
  373.       --version       output version information and exit\n\
  374. ");
  375.     }
  376.   exit (status);
  377. }
  378.